package cn.turing.firecontrol.server.modbus;

/**
 * @ClassName SQLAspect
 * @Description TODO
 * @Author MQ
 * @Date 2022/6/27 20:59
 */
import cn.turing.firecontrol.server.modbus.AuditAction;
import cn.turing.firecontrol.server.modbus.Action;
import cn.turing.firecontrol.server.modbus.JacksonSerializer;
import cn.turing.firecontrol.server.modbus.AuditLogDao;
import cn.turing.firecontrol.server.modbus.AuditLog;
import cn.turing.firecontrol.server.modbus.DeviceSensor;
import cn.turing.firecontrol.server.modbus.AuditLogService;
import cn.turing.firecontrol.server.modbus.DeviceSensorService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;

@Slf4j
@Aspect
@Component
public class SQLAspect {

    @Autowired
    private DeviceSensorService deviceSensorService;
    @Autowired
    private AuditLogService auditLogService;

    private AuditLog addAudioLog(Object obj, String type, String tableName, Integer srcNum) {
        String jsonString = JacksonSerializer.toJSONString(obj);
        AuditLog audioLog = AuditLog.builder().logData(jsonString).logTableName(tableName)
                .logType(type).srcNum(srcNum).createDt(new Date()).build();
        auditLogService.insert(audioLog);
        return audioLog;
    }



    /**
     * 拦截ADD操作，记录新增的数据
     * @param joinPoint
     */
    @After(value = "execution(public * com.login.springboot.dao..*.insert*(..))")
    public void doAfter(JoinPoint joinPoint) {
        // 获取该方法上的 @AuditAction注解
        AuditAction audioAction = this.getAudioActionByJoinPoint(joinPoint);
        if (audioAction != null && audioAction.action() == Action.ADD) {
            Object obj = joinPoint.getArgs()[0];
            this.addAudioLog(obj, AuditLogDao.ADD, audioAction.targetTable(), null);
        }
    }

    /**
     * 拦截UPDATE操作，记录更新前后的数据
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(value = "execution(public * com.login.springboot.dao..*.update*(..))")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        AuditAction audioAction = this.getAudioActionByJoinPoint(pjp);
        Object proceed = null;
        if (audioAction != null && audioAction.action() == Action.UPDATE) {
            String uuid = UUID.randomUUID().toString();
            Object originalObj = null;
            Object arg = pjp.getArgs()[0];
            String targetTable = audioAction.targetTable();
            switch (targetTable) {
                case "device_sensor":
                    DeviceSensor deviceSensor = (DeviceSensor) arg;
                    originalObj = deviceSensorService.selectByPrimaryKey(deviceSensor.getId());
                    break;
            }
            AuditLog auditLog = null;
            if (originalObj != null) {
                // TODO 在执行原方法之前，记录旧数据
                auditLog = this.addAudioLog(originalObj, AuditLogDao.UPDATE, targetTable, null);
            }
            // 执行原方法
            proceed = pjp.proceed();
            // TODO 在执行原方法之后，记录新数据
            if (auditLog != null) {
                this.addAudioLog(arg, AuditLogDao.UPDATE, targetTable, auditLog.getLogNum());
            }
        }
        if (proceed == null) {
            return pjp.proceed();
        }
        return proceed;
    }
    /**
     * 拦截DELETE操作，记录被删除的数据
     * @param joinPoint
     */
    @Before(value = "execution(public * com.login.springboot.dao..*.delete*(..))")
    public void doBefore(JoinPoint joinPoint) {
        // 获取方法中的参数
        Object[] args = joinPoint.getArgs();
        // 获取该方法上的 @AuditAction注解
        AuditAction audioAction = this.getAudioActionByJoinPoint(joinPoint);
        if (audioAction != null && audioAction.action() == Action.DELETE) {
            Object obj = null;
            String targetTable = audioAction.targetTable();
            switch (targetTable) {
                case "device_sensor":
                    long id = (long) args[0];
                    obj = deviceSensorService.selectByPrimaryKey(id);
                    break;
            }
            if (obj != null) {
                this.addAudioLog(obj, AuditLogDao.DELETE, targetTable, null);
            }
        }
    }


    /**
     * 根据JoinPoint对象获取目标方法上的注解
     * @param joinPoint
     * @return AuditAction
     */
    private AuditAction getAudioActionByJoinPoint(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Method[] methods = joinPoint.getSignature().getDeclaringType().getMethods();

        Method tempMethod = Arrays.stream(methods)
                .filter(m -> m.getName().equals(methodName))
                .filter(m -> Objects.equals(m.getParameterTypes().length, joinPoint.getArgs().length))
                .findFirst()
                .orElse(null);
        if (tempMethod != null) {
            AuditAction audioAction = (AuditAction) Arrays.stream(tempMethod.getDeclaredAnnotations())
                    .filter(a -> a instanceof AuditAction)
                    .findFirst()
                    .orElse(null);
            if (audioAction != null ) {
                return audioAction;
            }
        }
        return null;
    }
}